{# Base layout every page extends. Blocks: title, favicon, head_extra, navbar, page_body, content, footer, scripts. Override ``content`` for an ordinary page; reach for ``page_body`` only when a page needs to replace the whole main wrapper. ``#app-content`` is the element htmx swaps fragments into (see ``render()`` in rendering.py and the two layouts under layouts/). #} {% from "components/macros/feedback.html" import toast_region %} {% from "components/macros/layout.html" import appearance_head, theme_toggle, dialog, drawer %} {% from "components/macros/icons.html" import sprite %} {% block title %}{{ project_name }}{% endblock %} {# Favicon: the Aegis shield, a PNG with its own transparency (it reads on a light tab or a dark one), and the larger one iOS asks for when the page is added to a home screen. Bots that auto-request /favicon.ico will 404, which is harmless. #} {% block favicon %} {% endblock %} {# Tailwind + DaisyUI, precompiled — no runtime JIT, no CDN. Built into static/dist by the asset pipeline; static() returns the fingerprinted URL once a build exists and the raw source path before then. The theme script ahead of it applies the stored theme before the stylesheet paints (see static/js/theme.js). #} {{ appearance_head(default_theme) }} {# htmx. History snapshots are disabled deliberately: htmx's default back-button behavior saves the live DOM to localStorage and re-injects it on history navigation. This DOM is full of Alpine-expanded templates (x-if/x-for clones, x-teleport copies) and script tags — restoring a snapshot re-runs scripts ("identifier already declared") and makes Alpine re-expand already-expanded templates, duplicating the whole page once per back/forward press. historyCacheSize 0 makes every restore a cache miss; refreshOnHistoryMiss turns a miss into a clean full-page load instead of an AJAX restore. #} {# responseHandling (first matching rule wins, RegExp.test semantics): a 422 carries a re-rendered form and must swap (app.js toasts one that is JSON instead); other 4xx/5xx never swap and raise htmx:responseError, which app.js turns into a toast. #} {# SSE extension (hx-ext="sse") for job progress streams; registers against the global htmx, so it must follow core. #} {# Alpine.js — the collapse plugin MUST load before core so its directive is registered when Alpine inits. Both deferred, plugin first. #} {# Custom styles #} {% block head_extra %}{% endblock %} {# The bar along the top while a page or an htmx request loads (app.js). #} {# The icons ``icon()`` draws (a copy button, the chat's controls), once a page. #} {{ sprite() }} {% block navbar %}
{{ project_name }}
{{ theme_toggle() }} {% if meetings_page is defined %}Meetings{% endif %} {% if auth_enabled %}Overseer{% endif %} Dashboard
{% endblock %} {% block page_body %}
{% block content %}{% endblock %}
{% endblock %} {% block footer %}{% endblock %} {{ toast_region() }} {{ dialog() }} {{ drawer() }} {% if chat_path is defined %}{% endif %} {% if auth_enabled %}{% endif %} {% block scripts %}{% endblock %}